from picshell.engine.util.Format import Format
from picshell.engine.core.PicBuilder import PicBuilder

#
# 16F877 State Impl 
#

class State:
    PROGRAM_MEMORY_SIZE = 8192
    EEPROM_DATA_MEMORY = 256
    
    INDF = 0
    PCL = 2
    STATUS = 3
    FSR = 4
    PORTA = 5
    PORTB = 6
    PORTC = 7
    PORTD = 8
    PORTE = 9
    
    PCLATH = 0xA
    INTCON = 0xB
    
    EECON1 = 0x0C # in bk 3
    EECON2 = 0x0D # in bk 3
    EEADR = 0x0D  # in bk 2 (0x10D)
    EEADRH = 0x0F  # in bk 2 (0x10F)
    EEDATA = 0x0C # in bk 2 (0x10C)
    EEDATH = 0x0E # in bk 2 (0x10E)
    
    ADCON0 = 0x1F # in bk 0
    ADCON1 = 0x1F # in bk 1
    ADRESH = 0x1E # in bk 0
    ADRESL = 0x1E # in bk 1
    
    savedRegs = (INDF,PCL,STATUS,FSR,PCLATH,INTCON)
    
    def __init__(self):
        self.w=0 # the w register 
        self.bk = [0]* ((0x7F * 4)) # int[] internal registers (4 banks)
        self.eeData = [0]*State.EEPROM_DATA_MEMORY  # int[]
        self.eeWriteStep = 0    # for EECON2 write seq (0x55, 0xAA)
        self.pc = 0            # program counter
        self.pcstack = [0]*8   # int[] pc stack 
        self.level = 0         # pc stack level
        self.nbExecuted = 0 # for stats, nbr instructions executed
        self.monitors = []  # single address monitors
        self.globalWriteMonitors =[] # all addresses monitors    
        self.globalReadMonitors =[] # all addresses monitors    
        self.adc = [0]*8 # adc values, filled by callback pos 0 = AN0, pos1 = AN1 ...
        self.uartProvider = None  
        self.uartReciever = None
        self.builder = PicBuilder()
        # init register map (could be optimized now... as user register are inited to 0xFF..)
        for i in range (0, len(self.bk)):
             self.bk[i] = 0x00 
        
        # bank 0 0x20..0x7F user register to 0xFF
        for i in range (0x20, 0x80):
             self.bk[i] = 0xFF
        # bank 1 0xa0..0xef user register to 0xFF
        for i in range (0xa0,0xf0 ):
             self.bk[i] = 0xFF 
        # bank 2 0x120..0x16f user register to 0xFF
        for i in range (0x120,0x170 ):
             self.bk[i] = 0xFF 
        # bank 3 0x1a0..0x1ef user register to 0xFF
        for i in range (0x1a0,0x1f0 ):
             self.bk[i] = 0xFF 
             
        # init eeprom
        for i in range (0,len(self.eeData)):
            self.eeData[i] = 0xFF  
        
        self.bk[5] = 0xFF # porta
        self.bk[6] = 0xFF # portb
        self.bk[7] = 0xFF # portc
        self.bk[8] = 0xFF # portd
        self.bk[9] = 0xFF # porte

    def getW(self):
        return self.w
   
    def setW(self,w):
        self.w = w;
    
    def getZ(self):
        return (self.bk[self.STATUS]&4)>0
    
    def setZ(self,z):
        if (z):
            self.regWrite(self.STATUS, self.bk[self.STATUS] | 4)  # set
        else:
            self.regWrite(self.STATUS,self.bk[self.STATUS] & 251)  # clear

    def getC(self):
        return (self.bk[self.STATUS]&1)>0
    
    def setC(self,c):
         if (c):
             self.regWrite(self.STATUS,self.bk[self.STATUS] | 1)  # set 
         else:
             self.regWrite(self.STATUS,self.bk[self.STATUS] & 254) # clear 
    
    #
    # write to register
    #
    def regWrite(self, reg, value):
        value = value & 0xFF
        pclModified = False
        bank = self.bank();
        
        if (reg == 0):
            indf = self.bk[self.FSR]&0x7F # l'adresse ind. est dans FSR
            if indf != 0: # to be safe...
                #self.regWrite(indf,self.getW())
                self.bk[indf+(self.fsrbank()*0x80)] = self.getW()
       
        # some bytes are avaiable in multi bank
        elif (reg in self.savedRegs): 
            self.bk[reg] = value
            self.bk[0x80+reg] = self.bk[reg]
            self.bk[0x100+reg] = self.bk[reg]
            self.bk[0x180+reg] = self.bk[reg]  
          
        elif (reg == self.PORTB):
            if ((self.bank()== 0) or (self.bank()== 2)): # PORTB
                # only update pin output if pin is actually configured as an output
                trisb = self.bk[0x86]
                for bit in [1,2,4,8,16,32,64,128]:
                    if (trisb & bit)== 0 :
                        # 0 = output
                        nbit = value & bit
                        if nbit == 0:
                            self.bk[self.PORTB] = self.bk[self.PORTB] & (255-bit)
                        else:
                            self.bk[self.PORTB] = self.bk[self.PORTB] | bit
                #self.bk[self.PORTB] = value
                self.bk[0x100+self.PORTB] =self.bk[self.PORTB]
                value = self.bk[self.PORTB]
            elif ((self.bank()==1) or (self.bank()==3)):# TRISB
                self.bk[0x86] = value
                self.bk[0x186] = value
                
        elif (self.bank()== 0) and ((reg == self.PORTA) or (reg == self.PORTC) or (reg == self.PORTD) or (reg == self.PORTE)) :
                # only update pin output if pin is actually configured as an output
                trisa = self.bk[0x80+reg]
                for bit in [1,2,4,8,16,32,64,128]:
                    if (trisa & bit)== 0 :
                        # 0 = output
                        nbit = value & bit
                        if nbit == 0:
                            self.bk[reg] = self.bk[reg] & (255-bit)
                        else:
                            self.bk[reg] = self.bk[reg] | bit
                value = self.bk[reg]
                
        else:
            self.bk[reg+(self.bank()*0x80)] = value   
                   
        #
        # Register has a specific meaning ?
        #
        
        if(reg==self.PCL):
            #
            # PCL modified ? i.e computed goto
            #
            pcl = self.regRead(self.PCL)
            pclath = self.regRead(self.PCLATH)
            pclath = pclath << 8
            self.pc = pcl+(pclath&0x1FFF) # take only bit4..0 of pclath as pc is 13 bit wise
            pclModified = True
        #
        # ADC Simulation
        #
        elif (reg == self.ADCON0) and bank==0 :
            if value&0x4 >0 : # bit2 GO
                #start the conversion
                chanelToRead = (value & 0x38) >> 3 #00XX X000 chs2..chs0
                #print self.bk[self.ADCON1]
                adfm = ((self.bk[0x80+self.ADCON1] & 0x80) > 0)
                adrsh = 0
                adrsl = 0
                
                #self.adc is need to be filled by the component (potentiometer) on the ui side 
                if (adfm) :
                    # right justified 6 most significant bit of ADRESH are read as 0
                    adrsh = (self.adc[chanelToRead] >> 8) & 0x03 # only 2 bit
                    adrsl = self.adc[chanelToRead] & 0xFF # 8 bit
                else :
                    # left justified 6 least significant bit of ADRESL are read as 0
                    adrsh = (self.adc[chanelToRead] >> 2) & 0xFF 
                    adrsl = (self.adc[chanelToRead] & 0x03)<< 6 
                    
                self.abswrite(self.ADRESH, adrsh)
                self.abswrite(self.ADRESL+0x80, adrsl)
                #self.bk[self.ADRESH] = adrsh;
                #self.bk[self.ADRESL+0x80] = adrsl;
                
                self.bk[self.ADCON0] = value & (0xFF-4) # auto clear bit 2 GO
                
        #
        # EEPROM
        #
        elif (reg==self.EECON2):
           
            if (bank==3):
                if ((self.eeWriteStep == 0) and (self.bk[0x180+self.EECON2]==0x55)):
                    self.eeWriteStep = self.eeWriteStep +1   
                elif ((self.eeWriteStep == 1) and (self.bk[0x180+self.EECON2]==0xAA)):
                    self.eeWriteStep = self.eeWriteStep +1    
        elif (reg==self.EECON1):
            if (bank==3):
                #
                # DATA EEPROM READ/WRITE
                # very basic impl... yet functional for my needs till now...
                #
                eepgd = (self.bk[self.EECON1+0x180] >> 7)&0xFF; # programm or data memory ? (bk3)
                rd = self.bk[self.EECON1+0x180]&1
                wr = (self.bk[self.EECON1+0x180]&2)>>1
                wren = (self.bk[self.EECON1+0x180]&4)>>2
                eeadr = self.bk[0x100+self.EEADR] # get the address(bk2)
                if (eepgd == 0 ):
                    # 0 -> data memory
                    if (rd == 1):
                        # READ
                        self.bk[0x100+self.EEDATA]= self.eeData[eeadr&0xFF] # read eeprom -> data register
                            
                        # RD set back to 0
                        self.bk[self.EECON1+0x180] &= 0xFE
                    elif ((wren==1) and (wr == 1) and (self.eeWriteStep==2)):
                        #WRITE
                        self.eeData[eeadr&0xFF] = self.bk[0x100+self.EEDATA]&0xFF # write
                        #WR set back to 0
                        self.bk[self.EECON1+0x180] &= 0xFD
                        self.eeWriteStep = 0
                else:
                    # program memory
                    adrL = Format.bin(self.absreg(0x100+self.EEADR))
                    adrH = Format.bin(self.absreg(0x100+self.EEADRH))
                    adr =  int(adrH+adrL,2) & 0x1FFF # 13 bit adresse
                    
                    if (rd == 1):
                        (dataH,dataL) = self.readProgramMem(adr)
                        self.abswrite(0x100+self.EEDATA, dataL)
                        self.abswrite(0x100+self.EEDATH, dataH)
                        
                        # RD set back to 0
                        self.bk[self.EECON1+0x180] &= 0xFE
                    
                    elif ((wren==1) and (wr == 1) and (self.eeWriteStep==2)):
                        
                        dataL =  Format.bin(self.absreg(0x100+self.EEDATA))
                        dataH = Format.bin(self.absreg(0x100+self.EEDATH))
                        data = int(dataH+dataL,2) & 0x3FFF # 14 bit data
                        self.writeProgramMem(adr,Format.bin(data))
                        
                        #WR set back to 0
                        self.bk[self.EECON1+0x180] &= 0xFD
                        self.eeWriteStep = 0
                    
        #
        # watching any register ?
        #
        for monitor in self.monitors:
            if (monitor.address == (reg+0x80*bank)):
                monitor.execute(value);
        for monitor in self.globalWriteMonitors:
            monitor.execute((reg+0x80*bank),value,self);   
        
        return pclModified
 
    #
    # Register read
    #
    def regRead(self,reg):
        
        # hack to unlock ADCON0:G0
        #if (reg == 0x1F):
        #    self.bk[0x1F] = self.bk[0x1F] &(255-4) # force ADCON:GO ( bit 2) to 0
        
        if (reg+(self.bank()*0x80)) == 0x0C: #PIR1
            data = self.bk[0x0C]
            if self.uartProvider != None and  self.uartProvider.hasData() :
                data = data | 32 # RCIF  
            if self.uartReciever != None and  self.uartReciever.isReady() :
                data = data | 16 # TXIF              
            return data
        
        elif (reg+(self.bank()*0x80)) == 0x1A: #RCREG
            data = self.bk[0x1A]  
            if self.uartProvider != None and  self.uartProvider.hasData() :
                data =  self.uartProvider.getNext()          
            return data 
        elif (reg == 0):
            indf = self.bk[self.FSR]&0x7F # l'adresse ind. est dans FSR
            return self.bk[indf+(self.fsrbank()*0x80)] 
        
        #port a..e ?
        elif reg in (5,6,7,8,9) and len(self.globalReadMonitors)>0 :
            value = self.bk[reg+(self.bank()*0x80)]
            for monitor in self.globalReadMonitors:
                value = monitor.execute(reg,self,value); # will be updated by the monitor
                                                         # used for external device like buttons, switch...
                                                         # these external register have no callback, so I must
                                                         # update reg when I discover the change ... i.e when I read
                                                         # the port the device is on...
                self.regWrite(reg+(self.bank()*0x80), value)
            return value
        
        else:
            return self.bk[reg+(self.bank()*0x80)] # adressage direct
    
    # very specific use,
    # try to use regRead instead
    # Register read (with no bank handeling)
    #
    def absreg(self, reg):
        value = self.bk[reg]
        if reg in (5,6,7,8,9) and len(self.globalReadMonitors)>0 : # for devices...
            for monitor in self.globalReadMonitors:
                value = monitor.execute(reg,self,value); 
                self.abswrite(reg, value)
        return value
    
    # very specific use,
    # try to use regWrite instead 
    #
    def abswrite(self, reg,value):
        value = value & 0xFF
        self.bk[reg] = value
        
        #
        # watching any register ?
        #
        
        for monitor in self.monitors:
            if (monitor.address == reg):
                monitor.execute(value);
                
        for monitor in self.globalWriteMonitors:
            monitor.execute(reg,value,self); 
    
    
    #
    # BANK 
    #
    def bank(self):
        return (self.bk[self.STATUS] >> 5)&3
        #return (self.bk[self.STATUS] >> 5)&3;
    
    #
    # BANK for indirect addressing (FSR and INDF)
    #
    def fsrbank(self):
        irp = ((self.bk[self.STATUS] & 128)>0) # fsr bank info is stored in status byte bit 7
        fsrb7 = ((self.bk[self.FSR] & 128)>0)  # and fsr reg. bit 7
        if ((not fsrb7) and (not irp)):
            return 0
        elif (fsrb7 and (not irp)) :
            return 1
        elif ((not fsrb7) and irp) :
            return 2
        else:
            return 3
    #
    # internal PC 
    #
    #    
    def getPc(self):
        return self.pc
    
    def setPc(self,i):
        self.pc = i
        self.bk[self.PCL]=(self.pc+1)&0xFF
        self.bk[0x80+self.PCL]=self.bk[2]
        self.bk[0x100+self.PCL]=self.bk[2]
        self.bk[0x180+self.PCL]=self.bk[2]
    
    def incPc(self):
        self.pc += 1
        self.bk[self.PCL]=self.pc&0xFF
        self.bk[0x80+self.PCL]=self.bk[self.PCL]
        self.bk[0x100+self.PCL]=self.bk[self.PCL]
        self.bk[0x180+self.PCL]=self.bk[self.PCL]
    
    #
    # compute next pc for Goto and Call regarding to pclath 
    #
    def changePcForCallGoto(self, adr):
        # manage PCLATH bit 4:3
        #pclath = self.regRead(self.PCLATH)
        # 24 = 00011000
        pclath =  (self.bk[self.PCLATH]&24) << 8
        self.setPc((adr&0x7FF) + (pclath &0x1FFF))
    
    #
    # Usefull for Stats
    #
    def getNbExecuted(self):
        return self.nbExecuted
    
    def incNbExecuted(self):
        self.nbExecuted = self.nbExecuted+1;
    
    def getMonitors(self):
        return self.monitors
    
    def setMonitors(self,monitors):
        self.monitors = monitors
    
    def getLevel(self):
        return self.level;
    
    def setLevel(self,i):
        self.level = i;
    
    def pushStack(self):
        self.pcstack[self.level] = self.getPc()
        self.level = self.level + 1
        if (self.level == 8):
            self.level = 0
    
    def popStack(self):
        self.level = self.level -1
        if (self.level < 0):
            self.level = 7
        return self.pcstack[self.level]

    # bit must be in the form bit0 -> 1
    # bit1 -> 2
    # bit2 -> 4
    # bit3 -> 8
    # ...
    def isOutput(self,port,bit):
        return ((self.bk[0x80+port] & bit) >0)
        
    def getBuilder(self):
        return self.builder; # 14 bit Builder
    